# import threading  # 用于创建和管理线程
# import time  # 用于模拟耗时操作
# import random  # 用于生成随机数（模拟不同任务耗时）
#
# # 创建线程锁：解决多线程同时打印时内容混乱的问题
# # 原理：同一时间只允许一个线程执行print操作
# print_lock = threading.Lock()
#
#
# def task(task_id, sleep_time):
#     """
#     线程要执行的具体任务函数
#     :param task_id: 任务唯一标识（用于区分不同任务）
#     :param sleep_time: 模拟任务执行耗时（秒）
#     """
#     # 模拟任务执行过程（如接口请求、数据计算等耗时操作）
#     time.sleep(sleep_time)
#
#     # 使用with语句获取锁：进入代码块时自动获取锁，离开时自动释放
#     # 确保多线程打印内容不会交叉混乱
#     with print_lock:
#         # 输出任务结果：包含任务ID、耗时、执行线程ID
#         print(f"任务 {task_id} 执行完成，耗时 {sleep_time:.2f} 秒（线程ID：{threading.current_thread().ident}）")
#
#
# def basic_thread_demo():
#     """基础线程创建与管理示例：手动创建线程、启动线程、等待线程完成"""
#     print("=== 基础线程操作示例 ===")
#     threads = []  # 用于存储线程对象，方便后续管理
#
#     # 创建3个线程，分别执行不同任务
#     for i in range(3):
#         # 随机生成0.5-2.0秒的任务耗时（模拟不同任务的执行时间差异）
#         sleep = random.uniform(0.5, 2.0)
#         # 创建线程对象：target指定任务函数，args传递函数参数
#         t = threading.Thread(target=task, args=(i, sleep))
#         threads.append(t)  # 保存线程对象
#         t.start()  # 启动线程（此时线程开始执行task函数）
#
#     # 等待所有线程执行完毕（主线程阻塞在此，直到所有子线程完成）
#     for t in threads:
#         t.join()  # 等待当前线程结束
#
#     print("所有任务执行完成\n")  # 所有子线程完成后，主线程继续执行
#
#
# def thread_pool_demo():
#     """线程池示例：使用线程池管理线程，简化多线程操作"""
#     print("=== 线程池操作示例 ===")
#     # 从concurrent.futures导入ThreadPoolExecutor（线程池工具）
#     from concurrent.futures import ThreadPoolExecutor
#
#     # 创建线程池：max_workers=2表示最多同时运行2个线程
#     # with语句会自动管理线程池生命周期：任务完成后自动关闭线程池
#     with ThreadPoolExecutor(max_workers=2) as executor:
#         # 提交4个任务到线程池（线程池会自动调度空闲线程执行任务）
#         for i in range(4):
#             sleep = random.uniform(0.5, 2.0)  # 随机任务耗时
#             # 提交任务到线程池：参数为任务函数和函数参数
#             executor.submit(task, i, sleep)
#
#     # 线程池上下文管理器退出时，会自动等待所有任务完成
#     print("所有任务执行完成")
#
#
# if __name__ == "__main__":
#     # 先执行基础线程示例，展示线程创建的基本流程
#     basic_thread_demo()
#     # 再执行线程池示例，展示更高效的多线程管理方式
#     thread_pool_demo()
#
#
#
#



import os
import subprocess
import threading  # 新增：用于获取线程ID
from concurrent.futures import ThreadPoolExecutor

from config.constant import (
    REPORTS_DIR,
    REPORTS_TEMP_DIR,
    ALLURE_REPORT_DIR,
    TESTS_DIR
)


def ensure_dirs_exist():
    """确保所有必要的目录存在，避免执行时因路径不存在报错"""
    for dir_path in [REPORTS_DIR, REPORTS_TEMP_DIR, ALLURE_REPORT_DIR]:
        os.makedirs(dir_path, exist_ok=True)
    print(f"已确保必要目录存在：{[REPORTS_DIR, REPORTS_TEMP_DIR, ALLURE_REPORT_DIR]}")


def collect_test_files():
    """收集TESTS_DIR目录下所有符合命名规范的测试文件（test_*.py）"""
    test_files = []
    if os.path.exists(TESTS_DIR):
        for root, _, files in os.walk(TESTS_DIR):
            test_files.extend(
                os.path.join(root, f)
                for f in files
                if f.startswith("test_") and f.endswith(".py")
            )
    print(f"共找到 {len(test_files)} 个测试文件")
    return test_files


def run_single_test(test_file):
    """
    执行单个测试文件，每个线程生成独立的临时报告片段
    解决编码问题：指定utf-8编码并忽略无法解码的字符
    """
    # 为当前线程生成唯一临时目录（用线程ID区分，避免冲突）
    thread_temp_dir = os.path.join(REPORTS_TEMP_DIR, f"thread_{threading.get_ident()}")
    os.makedirs(thread_temp_dir, exist_ok=True)

    # 构建pytest命令
    cmd = [
        "python", "-m", "pytest",
        test_file,
        "-s",
        "--alluredir", thread_temp_dir,
        "--clean-alluredir"
    ]
    print(f"执行测试：{test_file}")

    # 关键修复：指定encoding='utf-8'，并设置errors='ignore'忽略无法解码的字符
    result = subprocess.run(
        cmd,
        capture_output=True,
        text=True,
        encoding='utf-8',  # 强制使用utf-8编码读取输出
        errors='ignore'  # 忽略无法解码的字符（避免报错）
    )

    # 判断测试是否成功
    if result.returncode == 0:
        print(f"测试成功：{test_file}")
        return True
    else:
        print(f"测试失败：{test_file}\n错误信息：{result.stderr}")
        return False


def run_tests_with_threading(num_threads=4):
    """多线程并行执行测试 + 生成Allure报告"""
    ensure_dirs_exist()
    test_files = collect_test_files()
    if not test_files:
        print("无测试文件可执行，退出")
        return

    # 多线程执行测试
    with ThreadPoolExecutor(max_workers=num_threads) as executor:
        results = list(executor.map(run_single_test, test_files))

    # 统计结果
    success_count = sum(1 for res in results if res)
    print(f"测试结果：成功 {success_count}/{len(test_files)}")

    # 合并生成Allure报告
    merge_cmd = f"allure generate {REPORTS_TEMP_DIR} -o {ALLURE_REPORT_DIR} --clean"
    subprocess.run(merge_cmd, shell=True, encoding='utf-8', errors='ignore')
    print(f"报告生成路径：{ALLURE_REPORT_DIR}\n可执行 `allure open {ALLURE_REPORT_DIR}` 查看")


if __name__ == "__main__":
    run_tests_with_threading(num_threads=4)

